// +----------------------------------------------------------------------
// | Update: 2022-12-06 10:24
// +----------------------------------------------------------------------
// | Author: Kerindax <1482152356@qq.com>
// +----------------------------------------------------------------------
// | Project Url: https://gitee.com/kerindax/UyghurCharUtils
// +----------------------------------------------------------------------


//! UyghurCharUtils - 维吾尔文字符处理工具库
//! 
//! 本库提供了维吾尔文字符的基本区到扩展区的转换功能，以及其他文本处理工具。
//! 
//! # 示例
//! 
//! ```
//! use uyghur_char_utils::UyghurCharUtils;
//! 
//! let utils = UyghurCharUtils::new();
//! let basic = "سالام";
//! let extended = utils.basic2extend(basic);
//! let back_to_basic = utils.extend2basic(&extended);
//! assert_eq!(basic, back_to_basic);
//! ```

use std::collections::HashMap;
use regex::Regex;
use lazy_static::lazy_static;

/// 基本区形式 A
pub const BASIC: usize = 0;
/// 单独形式 A
pub const ALONE: usize = 1;
/// 头部形式 A_
pub const HEAD: usize = 2;
/// 中部形式 _A_
pub const CENTR: usize = 3;
/// 后部形式 _A
pub const REAR: usize = 4;

/// 特殊字母组合结构
struct SpecialChar {
    basic: Vec<u32>,
    extend: Vec<u32>,
    link: Vec<u32>,
}

/// 维吾尔文字符处理工具
pub struct UyghurCharUtils {
    /// 字符映射表
    char_code: HashMap<char, Vec<char>>,
    /// 特殊字母组合列表
    special: Vec<SpecialChar>,
}

impl UyghurCharUtils {
    /// 创建一个新的UyghurCharUtils实例
    pub fn new() -> Self {
        let mut char_utils = UyghurCharUtils {
            char_code: HashMap::new(),
            special: vec![
                // LA
                SpecialChar {
                    basic: vec![0x644, 0x627],
                    extend: vec![0xfefc],
                    link: vec![0xfee0, 0xfe8e],
                },
                // _LA
                SpecialChar {
                    basic: vec![0x644, 0x627],
                    extend: vec![0xfefb],
                    link: vec![0xfedf, 0xfe8e],
                },
            ],
        };

        // 初始化字符映射表
        let char_data = [
            [0x626, 0xfe8b, 0xfe8b, 0xfe8c, 0xfe8c], // 1 --- 00-Hemze
            [0x627, 0xfe8d, 0xfe8d, 0xfe8e, 0xfe8e], // 0 --- 01-a   
            [0x6d5, 0xfee9, 0xfee9, 0xfeea, 0xfeea], // 0 --- 02-:e  
            [0x628, 0xfe8f, 0xfe91, 0xfe92, 0xfe90], // 1 --- 03-b   
            [0x67e, 0xfb56, 0xfb58, 0xfb59, 0xfb57], // 1 --- 04-p   
            [0x62a, 0xfe95, 0xfe97, 0xfe98, 0xfe96], // 1 --- 05-t   
            [0x62c, 0xfe9d, 0xfe9f, 0xfea0, 0xfe9e], // 1 --- 06-j   
            [0x686, 0xfb7a, 0xfb7c, 0xfb7d, 0xfb7b], // 1 --- 07-q   
            [0x62e, 0xfea5, 0xfea7, 0xfea8, 0xfea6], // 1 --- 08-h   
            [0x62f, 0xfea9, 0xfea9, 0xfeaa, 0xfeaa], // 0 --- 09-d   
            [0x631, 0xfead, 0xfead, 0xfeae, 0xfeae], // 0 --- 10-r   
            [0x632, 0xfeaf, 0xfeaf, 0xfeb0, 0xfeb0], // 0 --- 11-z   
            [0x698, 0xfb8a, 0xfb8a, 0xfb8b, 0xfb8b], // 0 --- 12-:zh 
            [0x633, 0xfeb1, 0xfeb3, 0xfeb4, 0xfeb2], // 1 --- 13-s   
            [0x634, 0xfeb5, 0xfeb7, 0xfeb8, 0xfeb6], // 1 --- 14-x   
            [0x63a, 0xfecd, 0xfecf, 0xfed0, 0xfece], // 1 --- 15-:gh 
            [0x641, 0xfed1, 0xfed3, 0xfed4, 0xfed2], // 1 --- 16-f   
            [0x642, 0xfed5, 0xfed7, 0xfed8, 0xfed6], // 1 --- 17-:k  
            [0x643, 0xfed9, 0xfedb, 0xfedc, 0xfeda], // 1 --- 18-k   
            [0x6af, 0xfb92, 0xfb94, 0xfb95, 0xfb93], // 1 --- 19-g   
            [0x6ad, 0xfbd3, 0xfbd5, 0xfbd6, 0xfbd4], // 1 --- 20-:ng 
            [0x644, 0xfedd, 0xfedf, 0xfee0, 0xfede], // 1 --- 21-l   
            [0x645, 0xfee1, 0xfee3, 0xfee4, 0xfee2], // 1 --- 22-m   
            [0x646, 0xfee5, 0xfee7, 0xfee8, 0xfee6], // 1 --- 23-n   
            [0x6be, 0xfbaa, 0xfbac, 0xfbad, 0xfbab], // 1 --- 24-:h  
            [0x648, 0xfeed, 0xfeed, 0xfeee, 0xfeee], // 0 --- 25-o   
            [0x6c7, 0xfbd7, 0xfbd7, 0xfbd8, 0xfbd8], // 0 --- 26-u   
            [0x6c6, 0xfbd9, 0xfbd9, 0xfbda, 0xfbda], // 0 --- 27-:o  
            [0x6c8, 0xfbdb, 0xfbdb, 0xfbdc, 0xfbdc], // 0 --- 28-v   
            [0x6cb, 0xfbde, 0xfbde, 0xfbdf, 0xfbdf], // 0 --- 29-w   
            [0x6d0, 0xfbe4, 0xfbe6, 0xfbe7, 0xfbe5], // 1 --- 30-e   
            [0x649, 0xfeef, 0xfbe8, 0xfbe9, 0xfef0], // 1 --- 31-i   
            [0x64a, 0xfef1, 0xfef3, 0xfef4, 0xfef2], // 1 --- 32-y 
            
            [0x6c5, 0xfbe0, 0xfbe0, 0xfbe1, 0xfbe1], // 0 --- kz o_
            [0x6c9, 0xfbe2, 0xfbe2, 0xfbe3, 0xfbe3], // 0 --- kz o^
            [0x62d, 0xfea1, 0xfea3, 0xfea4, 0xfea2], // 1 --- kz h
            [0x639, 0xfec9, 0xfecb, 0xfecc, 0xfeca], // 1 --- kz c
        ];

        for row in &char_data {
            let chars: Vec<char> = row.iter().map(|&code| char::from_u32(code).unwrap()).collect();
            for &ch in &chars {
                char_utils.char_code.insert(ch, chars.clone());
            }
        }

        char_utils
    }

    /// 获取对应字母
    fn get_char(&self, ch: char, index: usize) -> char {
        match self.char_code.get(&ch) {
            Some(chars) => chars[index],
            None => ch,
        }
    }

    /// 双目字母转换字符串
    fn get_string(&self, value: &[u32]) -> String {
        value.iter().map(|&code| char::from_u32(code).unwrap()).collect()
    }

    /// La字母转换扩展区
    fn extend_la(&self, source: &str) -> String {
        let mut result = source.to_string();
        for item in &self.special {
            let link_str = self.get_string(&item.link);
            let extend_str = self.get_string(&item.extend);
            result = result.replace(&link_str, &extend_str);
        }
        result
    }

    /// La字母转换基本区
    fn basic_la(&self, source: &str) -> String {
        let mut result = source.to_string();
        for item in &self.special {
            let extend_str = self.get_string(&item.extend);
            let basic_str = self.get_string(&item.basic);
            result = result.replace(&extend_str, &basic_str);
        }
        result
    }

    /// 对象反转
    fn reverse_subject(&self, str: &str) -> String {
        lazy_static! {
            static ref RE: Regex = Regex::new(r"(.+)").unwrap();
        }
        
        RE.replace_all(str, |caps: &regex::Captures| {
            caps[1].chars().rev().collect::<String>()
        }).to_string()
    }

    /// Ascii区反转
    fn reverse_ascii(&self, source: &str) -> String {
        lazy_static! {
            static ref SYMBOL_RE: Regex = Regex::new(r"[\)\(\]\[\}\{\>\<\»\«]").unwrap();
            static ref NOT_EXTEND_RE: Regex = Regex::new(r"[^\x{fb50}-\x{fdff}\x{fe70}-\x{feff}\s]+(\s[^\x{fb50}-\x{fdff}\x{fe70}-\x{feff}\s]+)*").unwrap();
        }

        let symbol_list: HashMap<char, char> = [
            (')', '('),
            ('(', ')'),
            (']', '['),
            ('[', ']'),
            ('}', '{'),
            ('{', '}'),
            ('>', '<'),
            ('<', '>'),
            ('»', '«'),
            ('«', '»'),
        ].iter().cloned().collect();

        NOT_EXTEND_RE.replace_all(source, |word: &regex::Captures| {
            let reversed = word[0].chars().rev().collect::<String>();
            SYMBOL_RE.replace_all(&reversed, |m: &regex::Captures| {
                let ch = m[0].chars().next().unwrap();
                match symbol_list.get(&ch) {
                    Some(&replacement) => replacement.to_string(),
                    None => ch.to_string(),
                }
            }).to_string()
        }).to_string()
    }

    /// 基本区转换扩展区
    /// 
    /// # Arguments
    /// 
    /// * `source` - 要转换的内容，可以包含混合字符串
    /// 
    /// # Returns
    /// 
    /// * 转换后的字符串
    pub fn basic2extend(&self, source: &str) -> String {
        lazy_static! {
            // 转换范围；不包含哈语的0x0621字母,问号,双引号和Unicode区域的符号
            static ref CONVERT_RE: Regex = Regex::new(r"[\x{0622}-\x{064a}\x{0675}-\x{06d5}]+").unwrap();
            // 分割范围，有后尾的字符表达式
            static ref SUFFIX_RE: Regex = Regex::new(r"[^\x{0627}\x{062F}-\x{0632}\x{0648}\x{0688}-\x{0699}\x{06C0}-\x{06CB}\x{06D5}]").unwrap();
            static ref ALONE_RE: Regex = Regex::new(r"(\S)").unwrap();
            static ref HEAD_RE: Regex = Regex::new(r"(\S)\s").unwrap();
            static ref CENTR_RE: Regex = Regex::new(r"\s(\S)\s").unwrap();
            static ref REAR_RE: Regex = Regex::new(r"\s(\S)").unwrap();
        }

        CONVERT_RE.replace_all(source, |word: &regex::Captures| {
            let mut returns = SUFFIX_RE.replace_all(&word[0], |m: &regex::Captures| {
                format!("{}{}", &m[0], "  ")
            }).to_string().trim().to_string();

            returns = ALONE_RE.replace_all(&returns, |m: &regex::Captures| {
                let ch = m[1].chars().next().unwrap();
                self.get_char(ch, ALONE).to_string()
            }).to_string();

            returns = HEAD_RE.replace_all(&returns, |m: &regex::Captures| {
                let ch = m[1].chars().next().unwrap();
                self.get_char(ch, HEAD).to_string()
            }).to_string();

            returns = CENTR_RE.replace_all(&returns, |m: &regex::Captures| {
                let ch = m[1].chars().next().unwrap();
                self.get_char(ch, CENTR).to_string()
            }).to_string();

            returns = REAR_RE.replace_all(&returns, |m: &regex::Captures| {
                let ch = m[1].chars().next().unwrap();
                self.get_char(ch, REAR).to_string()
            }).to_string();

            self.extend_la(&returns)
        }).to_string()
    }

    /// 扩展区转换基本区
    /// 
    /// # Arguments
    /// 
    /// * `source` - 要转换的内容
    /// 
    /// # Returns
    /// 
    /// * 转换后的字符串
    pub fn extend2basic(&self, source: &str) -> String {
        lazy_static! {
            // 扩展区范围；FB50-FDFF ->区域A    FE70-FEFF -> 区域B
            static ref EXTEND_RE: Regex = Regex::new(r"[\x{fb50}-\x{fdff}\x{fe70}-\x{feff}]").unwrap();
        }

        let source = self.basic_la(source);
        EXTEND_RE.replace_all(&source, |m: &regex::Captures| {
            let ch = m[0].chars().next().unwrap();
            self.get_char(ch, BASIC).to_string()
        }).to_string()
    }

    /// 基本区转换反向扩展区
    /// 
    /// # Arguments
    /// 
    /// * `source` - 要转换的内容
    /// 
    /// # Returns
    /// 
    /// * 转换后的字符串
    pub fn basic2r_extend(&self, source: &str) -> String {
        let extended = self.basic2extend(source);
        let reversed_subject = self.reverse_subject(&extended);
        self.reverse_ascii(&reversed_subject)
    }

    /// 反向扩展区转换基本区
    /// 
    /// # Arguments
    /// 
    /// * `source` - 要转换的内容
    /// 
    /// # Returns
    /// 
    /// * 转换后的字符串
    pub fn r_extend2basic(&self, source: &str) -> String {
        let reversed_ascii = self.reverse_subject(&self.reverse_ascii(source));
        self.extend2basic(&reversed_ascii)
    }

    /// 音节索引
    /// 
    /// # Arguments
    /// 
    /// * `source` - 要处理的内容
    /// 
    /// # Returns
    /// 
    /// * 处理后的字符串
    pub fn basic_syllable(&self, source: &str) -> String {
        lazy_static! {
            // 音节切开专用，取韵母
            static ref FINALS_RE: Regex = Regex::new(r"([\x{0627}\x{06d5}\x{0648}\x{06c7}\x{06c6}\x{06c8}\x{06d0}\x{0649}\x{06c9}\x{06c5}])([^\x{0627}\x{06d5}\x{0648}\x{06c7}\x{06c6}\x{06c8}\x{06d0}\x{0649}\x{06c9}\x{06c5}]+)(?=[\x{0627}\x{06d5}\x{0648}\x{06c7}\x{06c6}\x{06c8}\x{06d0}\x{0649}\x{06c9}\x{06c5}])").unwrap();
            static ref WORD_RE: Regex = Regex::new(r"[^\s]+").unwrap();
        }

        WORD_RE.replace_all(source, |word: &regex::Captures| {
            FINALS_RE.replace_all(&word[0], |m: &regex::Captures| {
                let m2_len = m[2].chars().count();
                let index = (m2_len / 2) as usize;
                let m2_chars: Vec<char> = m[2].chars().collect();
                let first_part: String = m2_chars[..index].iter().collect();
                let second_part: String = m2_chars[index..].iter().collect();
                format!("{}{} {}", &m[1], first_part, second_part)
            }).to_string()
        }).to_string()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_basic2extend() {
        let utils = UyghurCharUtils::new();
        // 测试基本区到扩展区的转换
        let basic = "سالام";
        let extended = utils.basic2extend(basic);
        assert_ne!(basic, extended);
    }

    #[test]
    fn test_extend2basic() {
        let utils = UyghurCharUtils::new();
        let basic = "سالام";
        let extended = utils.basic2extend(basic);
        let back_to_basic = utils.extend2basic(&extended);
        assert_eq!(basic, back_to_basic);
    }

    #[test]
    fn test_basic2r_extend() {
        let utils = UyghurCharUtils::new();
        let basic = "سالام";
        let r_extended = utils.basic2r_extend(basic);
        assert_ne!(basic, r_extended);
    }

    #[test]
    fn test_r_extend2basic() {
        let utils = UyghurCharUtils::new();
        let basic = "سالام";
        let r_extended = utils.basic2r_extend(basic);
        let back_to_basic = utils.r_extend2basic(&r_extended);
        assert_eq!(basic, back_to_basic);
    }

    #[test]
    fn test_basic_syllable() {
        let utils = UyghurCharUtils::new();
        let text = "سالام دۇنيا";
        let syllabled = utils.basic_syllable(text);
        assert_ne!(text, syllabled);
    }
}